package pro.lepers.portfolio.view.contents.portfolio {
	import caurina.transitions.Equations;
	import caurina.transitions.Tweener;

	import fr.digitas.flowearth.command.Batcher;
	import fr.digitas.flowearth.command.IBatchable;
	import fr.digitas.flowearth.core.IIterator;
	import fr.digitas.flowearth.event.NodeEvent;
	import fr.digitas.flowearth.mvc.address.structs.INode;
	import fr.digitas.flowearth.ui.layout.Layout;
	import fr.digitas.flowearth.ui.layout.renderer.TopWeakRenderer;

	import pro.lepers.portfolio.controls.KeyManager;
	import pro.lepers.portfolio.view.contents.portfolio.data.ProjectDatas;
	import pro.lepers.portfolio.view.contents.portfolio.data.ProjectProvider;

	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;
	import flash.utils.clearTimeout;
	import flash.utils.setTimeout;

	/**
	 * @author plepers
	 */
	public class ProjectFrise extends Sprite {
		
		
		public static const MARGIN : int = 65;
		
		public function ProjectFrise ( provider : ProjectProvider ) {
			
			_provider = provider;
			_build();
			_buildLayout();
			_buildItems( );
			
			addEventListener(MouseEvent.MOUSE_DOWN, mouseDown );
			
			addEventListener( Event.ADDED_TO_STAGE, onAdded );
			addEventListener( Event.REMOVED_FROM_STAGE, onRemoved );
			
			_provider.node.addEventListener( NodeEvent.CHILD_CHANGE , onItemActivate );
			onItemActivate( null );
			initKeyboard();
		}

		
		override public function set height (value : Number) : void {
			_trackBg.height = value;
			_trackBg.y = -y;
		}

		public function dispose() : void {
			disposeKeyboard();
			
			removeEventListener(MouseEvent.MOUSE_DOWN, mouseDown );
			removeEventListener( Event.ENTER_FRAME, _integratePysics );
			
			if( _currentNode ) {
				_currentNode.removeEventListener( NodeEvent.CHILD_CHANGE , onSubChange, true );
				_currentNode = null;
			}
			
			if( _batcher ) _batcher.dispose();

			_provider.node.removeEventListener( NodeEvent.CHILD_CHANGE , onItemActivate, true );
			_provider = null;
			
			while( _layout.numChildren > 0 )
				( _layout.removeChildAt( 0 ) as ProjectItem ).dispose();
			
		}
		
		private function onAdded( e : Event ) : void {
			addMouseWheel( );
			
			
			//onItemActivate( null );
		}

		private function onRemoved( e : Event ) : void {
			clearDrags();
			clearTimeout( _mouseWheelTmout );
			stage.removeEventListener( MouseEvent.MOUSE_WHEEL, mouseWheel );
			stage.removeEventListener( Event.ENTER_FRAME, mouseMove );
			stage.removeEventListener( MouseEvent.MOUSE_UP, mouseUp );
			_cancelClick = false;
			stage.removeEventListener( MouseEvent.CLICK, cancelClick, true );
			
			
		}
		
		
		private function onItemActivate(event : NodeEvent) : void {
			registerCurrentNode( _provider.node.getCurrentChild() || _provider.node.getDefaultChild() );
			if( _dragging || _physics ) return;
			focusItem( getCurrentItem() );
		}
		
		private function registerCurrentNode( node : INode) : void {
			if( _currentNode == node ) return;
			if( _currentNode ) {
				_currentNode.removeEventListener( NodeEvent.CHILD_CHANGE , onSubChange, true );
				_currentNode = null;
			}
			_currentNode = node;
			if( _currentNode ) {
				_currentNode.addEventListener( NodeEvent.CHILD_CHANGE , onSubChange );
				onSubChange( null );
			}
			
		}
		
		private function onSubChange(event : NodeEvent) : void {
			_contentOpen = _currentNode.getCurrentChild() != null;
			if( _contentOpen ) {
				focusItem( getCurrentItem() );
				Tweener.addTween( _trackBg, { width : ProjectItem.THW + 40, alpha: 0, time : ProjectItem.OPEN_EASE_TIME, transition : ProjectItem.OPEN_EASE, onComplete: _trackExpandComplete } );
				Tweener.addTween( _lContainer, { y : - ProjectItem.DIFF_HEIGHT>>1, time : ProjectItem.OPEN_EASE_TIME, transition : ProjectItem.OPEN_EASE } );
			} 
			else {
				addChildAt( _trackBg, 0 );
				Tweener.addTween( _trackBg, { width : 225, alpha: 1, time : ProjectItem.CLOSE_EASE_TIME, transition : ProjectItem.CLOSE_EASE } );
				Tweener.addTween( _lContainer, { y : 0, time : ProjectItem.CLOSE_EASE_TIME, transition : ProjectItem.CLOSE_EASE } );
			}
		}

		private function _trackExpandComplete () : void {
			if( _trackBg.parent )
				removeChild( _trackBg );
		}

		
		private function getCurrentItem() : ProjectItem {
			var nid : String = ( _provider.node.getCurrentChild() || _provider.node.getDefaultChild() ).getId();
			var item : ProjectItem;
			for (var i : int = 0; i < _layout.numChildren; i++) {
				item = _layout.getChildAt( i ) as ProjectItem;
				if( item.projectDatas._id == nid ) {
					return item;
					break;
				}
			}
			return null;
		}
		
		private function addMouseWheel() : void {
			clearTimeout( _mouseWheelTmout );
			stage.addEventListener( MouseEvent.MOUSE_WHEEL, mouseWheel );
		}

		private function _buildItems () : void {
			
			if( _batcher ) _batcher.dispose();
			
			_batcher = new Batcher();
			
			var _thumbBatcher : Batcher = new Batcher();
			var _contentBatcher : Batcher = new Batcher();
			
			_batcher.addItem( _thumbBatcher );
			_batcher.addItem( _contentBatcher );
			
			var iter : IIterator = _provider.getItems();
			var itemd : ProjectDatas;
			
			var item : ProjectItem;
			
			var mLoadables : IBatchable;
			var cLoadables : IBatchable;
			
			while( iter.hasNext() ) {
				itemd = iter.next() as  ProjectDatas;
				item = new ProjectItem();
				item.setDatas( itemd );
				mLoadables = item.getMainLoadable( );
				cLoadables = item.getContentLoadables();
				if( mLoadables )
					_thumbBatcher.addItem ( mLoadables  );
				if( cLoadables )
					_contentBatcher.addItem ( cLoadables );
				_layout.addChild( item );
			}
			
			_layout.update( );
			_lminY = - (_layout.numChildren-1 ) * ( ProjectItem.COLLAPSE_HEIGHT + MARGIN ) -65;
			
			_batcher.start();
		}

		private function _build () : void {
			
			_lastSpeeds = new Vector.<Number>( 3 );
			_lastSpeeds[ 0 ] = _lastSpeeds[1] = _lastSpeeds[2] = 0;
			
			_trackBg = new Shape();
			addChild( _trackBg );
			_trackBg.graphics.beginFill( 0, 0.16 );
			_trackBg.graphics.drawRect(-125, 0, 225, 500 );
//			_trackBg.blendMode = BlendMode.MULTIPLY;
		}

		private function _buildLayout () : void {
			_lContainer = new Sprite();
			addChild( _lContainer );
			
			_layout = new Layout( );
			_layout.renderer = new TopWeakRenderer( );
			_layout.margin = new Rectangle( 0,0,0, MARGIN );
			_layout.x = -95;
			vly = _lmaxY;
			_lContainer.addChild( _layout );
		}
		
		
		private function _checkActiveItem () : void {
			
			var cy : Number = - _layout.y - 180;
			var centeredItem : ProjectItem;
			for (var i : int = 0; i < _layout.numChildren; i++) {
				centeredItem = _layout.getChildAt( i ) as ProjectItem;
				if( centeredItem.y > cy ) break;
			}
			//throw new Error( "pro.lepers.portfolio.view.contents.portfolio.ProjectFrise - _checkActiveItem : " );
			centeredItem.projectDatas.node.activate();
			
		}

		
		private var _lContainer : Sprite;
		private var _layout : Layout;
		
		private var _provider : ProjectProvider;
		
		private var _batcher : Batcher;
		
		private var _trackBg : Shape;
		
		private var _currentNode : INode;
		
		private var _contentOpen : Boolean = false;

		//_____________________________________________________________________________
		//																		  FOCUS
		
		private function focusItem ( item : ProjectItem , direct : Boolean = false ) : void {
			var index : int = _layout.getChildIndex( item );
			var _y : int = - index * ( ProjectItem.COLLAPSE_HEIGHT + MARGIN )- 65;
			if( direct )
				vly = _y;
			else
				Tweener.addTween( this, { vly : _y, time : 0.4, transition : Equations.easeInOutQuad } );
		}

		//_____________________________________________________________________________
		//																		   KEYS
		
		private function initKeyboard() : void {
			KeyManager.instance.addKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.DOWN, nextProject );
			KeyManager.instance.addKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.UP, prevProject );
			KeyManager.instance.addKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.RIGHT, keyRight );
			KeyManager.instance.addKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.LEFT, keyLeft );
		}

		private function disposeKeyboard() : void {
			KeyManager.instance.removeKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.DOWN, nextProject );
			KeyManager.instance.removeKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.UP, prevProject );
			KeyManager.instance.removeKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.RIGHT, keyRight );
			KeyManager.instance.removeKeyListener( KeyboardEvent.KEY_DOWN, Keyboard.LEFT, keyLeft );
		}
		
	
		
		private function keyRight( e:  Event ) : void {
			getCurrentItem().next();
		}
		private function keyLeft( e:  Event ) : void {
			getCurrentItem().prev();
		}
		
		private function nextProject( e:  Event ) : void {
			if( _physics ) stopPhysics();
			var nid : String = ( _provider.node.getCurrentChild() || _provider.node.getDefaultChild() ).getId();
			var cIndex : int = _provider.getItemIndex( nid );
			var nItem : ProjectDatas = _provider.getItemAt( cIndex + 1 );
			if( nItem )	_provider.node.getChild( nItem._id  ).activate();
		}

		private function prevProject( e : Event ) : void {
			if( _physics ) stopPhysics();
			var nid : String = ( _provider.node.getCurrentChild() || _provider.node.getDefaultChild() ).getId();
			var cIndex : int = _provider.getItemIndex( nid );
			var nItem : ProjectDatas = _provider.getItemAt( cIndex - 1 );
			if( nItem )	_provider.node.getChild( nItem._id  ).activate( );
		}
		

		
		
		
		
		//_____________________________________________________________________________
		//																		   DRAG
		
		private var _lmaxY : Number = -65;
		private var _lminY : Number ;
		private var _vly : Number;
		
		
		private var _lf : Number = 0;
		private var _lv : Number = 0;
		
		private var _lastSpeeds : Vector.<Number>;
		private var _downMPos : Point;
		private var _downLY : Number;
		private var _cancelClick : Boolean = false;
		private var _mouseWheelTmout : int;
		
		private var _dragging : Boolean  = false;
		private var _physics : Boolean  = false;
		
		
		private function mouseDown (event : MouseEvent) : void {
			if( _contentOpen ) return;
			_dragging = true;
			Tweener.removeTweens( this );
			_downMPos = new Point( stage.mouseX, stage.mouseY );
			_downLY = _layout.y;
			stopPhysics();
			stage.addEventListener( Event.ENTER_FRAME, mouseMove );
			stage.addEventListener( MouseEvent.MOUSE_UP, mouseUp );
			stage.addEventListener( Event.MOUSE_LEAVE, mouseUp );
		}

		private function mouseMove (event : Event) : void {
			
			if( !_cancelClick && Point.distance( _downMPos, new Point( stage.mouseX, stage.mouseY ) ) > 4 ) {
				_cancelClick = true;
				stage.addEventListener(MouseEvent.CLICK, cancelClick, true, 1 );
			}
			
			var dy : Number = stage.mouseY - _downMPos.y;
			
			var ly : Number = _downLY + dy;
			
			var limdist : Number;
			
			if( _lmaxY < ly ) {
				limdist =  ly - _lmaxY;
				ly = _lmaxY + Math.pow( limdist, .85 );
			}
			else if ( ly < _lminY ) {
				limdist = _lminY - ly;
				ly = _lminY - Math.pow( limdist, .85 );
			}
			
			_lastSpeeds.shift();
			_lastSpeeds.push( ly - _vly );
			
			vly = ly;
			
			_checkActiveItem();
		}

		private function mouseUp (event : Event) : void {
			clearDrags();
			if( Point.distance( _downMPos, new Point( stage.mouseX, stage.mouseY ) ) < 4 ) return;
			_lv = ( _lastSpeeds[ 0 ] + _lastSpeeds[1] + _lastSpeeds[2] ) / 3;
			_lastSpeeds[ 0 ] = _lastSpeeds[1] = _lastSpeeds[2] = 0;
			Tweener.removeTweens( this );
			runPhysics();
		}
		
		private function clearDrags() : void {
			_dragging = false;
			stage.removeEventListener( Event.ENTER_FRAME, mouseMove );
			stage.removeEventListener( MouseEvent.MOUSE_UP, mouseUp );
			stage.removeEventListener( Event.MOUSE_LEAVE, mouseUp );
			setTimeout(removeClicCanceler, 10, stage );
		}

		private function removeClicCanceler ( s : Stage ) : void {
			_cancelClick = false;
			s.removeEventListener( MouseEvent.CLICK, cancelClick, true );
		}

		
		private function mouseWheel (event : MouseEvent) : void {
			if( _contentOpen ) return;
			clearDrags();
			stage.removeEventListener( MouseEvent.MOUSE_WHEEL, mouseWheel );
			clearTimeout( _mouseWheelTmout );
			setTimeout( addMouseWheel, 10 );
			Tweener.removeTweens( this );
			
			_lv = event.delta * 5;
			runPhysics();
		}
		
		private function runPhysics() : void {
			_dragging = false;
			_physics = true;
			addEventListener( Event.ENTER_FRAME, _integratePysics );
		}

		private function stopPhysics() : void {
			_physics = false;
			removeEventListener( Event.ENTER_FRAME, _integratePysics );
		}

		
		
		private function _integratePysics (event : Event) : void {

			if( _lmaxY < _vly ) {
				_lf = ( _lmaxY - _vly ) *0.05;
			}
			else if ( _vly < _lminY ) {
				_lf = -( _vly - _lminY ) * 0.05;
			} 
			else {
				_lf = 0;
			}
			
			_lv += _lf;
			
			if( ( Math.abs( _lf ) < 0.1 &&Math.abs( _lv ) < 0.1 ) || _dragging ) 
				stopPhysics();
			
			
			if( _lf != 0 )
				_lv *= 0.7;
				else
				_lv *= 0.9;
			
			vly += _lv;
			
			_checkActiveItem();
		}

		private function cancelClick (event : MouseEvent) : void {
			trace( "cancel clic" );
			event.stopImmediatePropagation( );
		}
		
		public function set vly (vly : Number) : void {
			_vly = vly;
			_layout.y = Math.round( vly );
		}
		
		public function get vly () : Number {
			return _vly;
		}
		
	}
}
